package core.comunication;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

import core.descriptors.TaskDescriptor;
import core.proxy.TQueueManagerProxyP2P;
import core.util.COREServiceRegistry;
/**
 *
 * Classe che gestisce l'accodamento dei task ricevuti al TQueueManager. 
 * @author Vincenzo Merlo, Scuderi Giovanni Luca, Paolo Giuffrida
 *
 */
public class UDPHandler implements Runnable {
	private int port;
	private final Logger logger = COREServiceRegistry.getLogger(TQueueManagerProxyP2P.class.toString());
		
	/**
	 * Costruttore di default che inizializza la variabile port
	 * @param port
	 */
	public UDPHandler (int port) {
		this.setPort(port);
	}
	
	/**
	 * Implementazione del metodo run dell'interfaccia runnable.
	 * Estrae un messaggio dalla testa della coda dei messaggi e, dopo aver effettuato l'unmarshalling del messaggio mediante la funziona unmarsh,
	 * ricrea l'oggetto Taskdescriptor e lo inserisce nella coda TQueueManager
	 */
	@Override
	public synchronized void run() {
		while(true) {
			try {
				TimeUnit.MILLISECONDS.sleep(10);   //safe the processors
				TaskDescriptor td = unMarsh(MessageServer.getMessages().take());
				logger.debug("ho effettuato l'unMarsh");
				logger.debug("Instanza di TQManager: " + MessageServer.getTQ());
				MessageServer.getTQ().enqueue(td);
				logger.debug("ho effettuato l'enqueue");
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	
	/**
	 * @param MSGTDEnqueue packet
	 * @return TaskDescriptor estratto dal pacchetto
	 * @throws ClassNotFoundException 
	 * @throws IOException 
	 */
	public TaskDescriptor unMarsh(MSGTDEnqueue packet) throws IOException, ClassNotFoundException {
		return unmarshHL(packet);
	}
	
	/**
	 * Funzione che si occupa dell'unmarshalling HL del messaggio secondo il seguente procollo: td(TaskDescripto)|timeCurrent(long). Dopo aver 
	 * estratto il TaskDescriptor contenuto nel messaggio, invia l'ack al chiamante inviando l'id del task e il timeCurrent ricevuto. 
	 * Il timecurrent rappresenta l'identificativo del messaggio.
	 * La funzione restituisce il TaskDescriptor contenunto nel messaggio
	 * @param MSGTDEnqueue packet
	 * @return TaskDescriptor
	 * @throws ClassNotFoundException 
	 * @throws IOException 
	 */
	protected TaskDescriptor unmarshHL(MSGTDEnqueue packet) throws IOException, ClassNotFoundException {
		response(packet.getTd(), packet.getTimeCurrent(), packet.getAddr());
		return packet.getTd();
	}
	
	/**
	 * Funzione che si occupa di costruire il messaggio di risposta (ack) che contiene il timeCurrent e l'id del taskDescriptor ricevuto
	 * Protocolo int TasckDescriptor.Id - long timeCurrent
	 * @param td
	 * @param timeCurrent
	 * @param inetAddress
	 * @throws IOException
	 */
	private void response(TaskDescriptor td, long timeCurrent, InetAddress inetAddress) throws IOException {
		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
	    ObjectOutputStream objectStream;
		objectStream = new ObjectOutputStream(byteOut);
		MSGTDEnqueueAck ack = new MSGTDEnqueueAck();
		ack.setCurrentTime(timeCurrent);
		ack.setId(td.getID());
		objectStream.writeObject(ack);

		DatagramPacket dp = new DatagramPacket(
				byteOut.toByteArray(), 
				byteOut.toByteArray().length,
				inetAddress,
				port
			);
		DatagramSocket ds = new DatagramSocket();
		
		logger.debug("***********Invio l'ack all'enqueue " +  td.getID() + " " + timeCurrent + " " + 
				inetAddress + ":" + port);
		
		ds.send(dp);
		
	}

	/**
	 * 
	 * @param port
	 */
	public void setPort(int port) {
		this.port = port;
	}

	/**
	 * 
	 * @return port
	 */
	public int getPort() {
		return port;
	}
	
}